package errors

import (
	"fmt"
	"runtime"
	"strings"
)

// ErrorType represents the type of error
type ErrorType string

const (
	// ErrorTypeValidation represents validation errors
	ErrorTypeValidation ErrorType = "validation"
	// ErrorTypeResource represents resource-related errors
	ErrorTypeResource ErrorType = "resource"
	// ErrorTypeSystem represents system-level errors
	ErrorTypeSystem ErrorType = "system"
	// ErrorTypeAI represents AI-related errors
	ErrorTypeAI ErrorType = "ai"
	// ErrorTypeNetwork represents network-related errors
	ErrorTypeNetwork ErrorType = "network"
)

// Error represents a structured error
type Error struct {
	Type    ErrorType
	Code    string
	Message string
	Cause   error
	Stack   string
	Data    map[string]interface{}
}

// Error implements the error interface
func (e *Error) Error() string {
	var sb strings.Builder
	sb.WriteString(fmt.Sprintf("[%s] %s: %s", e.Type, e.Code, e.Message))
	if e.Cause != nil {
		sb.WriteString(fmt.Sprintf("\nCaused by: %v", e.Cause))
	}
	return sb.String()
}

// WithData adds data to the error
func (e *Error) WithData(key string, value interface{}) *Error {
	if e.Data == nil {
		e.Data = make(map[string]interface{})
	}
	e.Data[key] = value
	return e
}

// New creates a new error
func New(errType ErrorType, code string, message string) *Error {
	return &Error{
		Type:    errType,
		Code:    code,
		Message: message,
		Stack:   getStackTrace(),
	}
}

// Wrap wraps an existing error
func Wrap(err error, errType ErrorType, code string, message string) *Error {
	return &Error{
		Type:    errType,
		Code:    code,
		Message: message,
		Cause:   err,
		Stack:   getStackTrace(),
	}
}

// Is checks if an error is of a specific type
func Is(err error, errType ErrorType) bool {
	if e, ok := err.(*Error); ok {
		return e.Type == errType
	}
	return false
}

// getStackTrace returns the current stack trace
func getStackTrace() string {
	const depth = 32
	var pcs [depth]uintptr
	n := runtime.Callers(3, pcs[:])
	frames := runtime.CallersFrames(pcs[:n])

	var sb strings.Builder
	for {
		frame, more := frames.Next()
		if !strings.Contains(frame.File, "runtime/") {
			sb.WriteString(fmt.Sprintf("\n\t%s:%d %s", frame.File, frame.Line, frame.Function))
		}
		if !more {
			break
		}
	}
	return sb.String()
}

// Common error codes
const (
	ErrCodeInvalidInput     = "INVALID_INPUT"
	ErrCodeResourceNotFound = "RESOURCE_NOT_FOUND"
	ErrCodeSystemError      = "SYSTEM_ERROR"
	ErrCodeAIError          = "AI_ERROR"
	ErrCodeNetworkError     = "NETWORK_ERROR"
)

// Common validation errors
func InvalidInput(message string) *Error {
	return New(ErrorTypeValidation, ErrCodeInvalidInput, message)
}

// Common resource errors
func ResourceNotFound(resource string) *Error {
	return New(ErrorTypeResource, ErrCodeResourceNotFound, fmt.Sprintf("%s not found", resource))
}

// Common system errors
func SystemError(message string) *Error {
	return New(ErrorTypeSystem, ErrCodeSystemError, message)
}

// Common AI errors
func AIError(message string) *Error {
	return New(ErrorTypeAI, ErrCodeAIError, message)
}

// Common network errors
func NetworkError(message string) *Error {
	return New(ErrorTypeNetwork, ErrCodeNetworkError, message)
}
